import traceback
from abc import ABC, abstractmethod
from collections.abc import Callable
from contextlib import contextmanager

import lldb


@contextmanager
def error_ctx():
    try:
        yield
    except BaseException:
        print(traceback.format_exc())


def error_handler(func):
    def wrapper(*args, **kwargs):
        with error_ctx():
            return func(*args, **kwargs)

    return wrapper


def get_parent_type_by_name(sbtype: lldb.SBType, typename: str) -> lldb.SBType | None:
    while sbtype.IsPointerType():
        sbtype = sbtype.GetPointeeType()
    if sbtype.GetName() == typename:
        return sbtype
    bases: list[lldb.SBTypeMember] = sbtype.get_bases_array()
    for base in bases:
        this_type: lldb.SBType = base.GetType()
        if this_type.GetName() == typename:
            return this_type

        base_type = get_parent_type_by_name(this_type, typename)
        if base_type:
            return base_type
    return None


def get_full_name(t: type | Callable) -> str:
    m = t.__module__
    if m == 'builtins':
        return t.__qualname__
    return f'{m}.{t.__qualname__}'


class IsTypeBase(ABC):
    @staticmethod
    @abstractmethod
    def is_type(sbtype: lldb.SBType, internal_dict: dict) -> bool:
        pass


class SummaryRecognizer(IsTypeBase):
    @staticmethod
    @abstractmethod
    def summary(valobj: lldb.SBValue, internal_dict: dict):
        pass


class SynthRecognizer(IsTypeBase):
    @staticmethod
    @abstractmethod
    def synth(valobj: lldb.SBValue, internal_dict: dict):
        pass


def dereference_value(value: lldb.SBValue) -> lldb.SBValue:
    t = value.GetType()
    if t.IsPointerType() or t.IsReferenceType():
        return dereference_value(value.Dereference())
    return value


def c_substr(quoted_text: str, length: int) -> str:
    result = ''
    pos = 1
    escape = False
    while length and pos < len(quoted_text):
        char = quoted_text[pos]
        result += char
        length -= 1
        if not escape and char == '\\':
            escape = True
        else:
            escape = False
            pos += 1
    return f'"{result}"'
